# USB Header script
#
# Copyright 2006-2012, Haiku.
# Distributed under the terms of the MIT License.
#
# Authors:
#		John Drinkwater, john@nextraweb.com
#
# Use with http://www.linux-usb.org/usb.ids
# run as: awk -v HEADERFILE=usbhdr.h -f usb-header.awk usb.ids

BEGIN {

	# field separator, defining because user could have overridden
	FS = " "

	# Pass this in from outside with -v HEADERFILE=filenametouse
	# we require usbhdr.h for our system
	ofile = HEADERFILE

	# possibly use this in the future
	cleanvalues = "[^A-Za-z0-9{}\"'&@?!*.,:;+<> \\t\\/_\\[\\]=#()-]"
	# ToDo: currently IDs aren't checked, we dumbly assume the source is clean

	inClassesDefinition = 0

	# descriptive output header
	print "#if 0" > ofile
	print "#\tUSBHDR.H: USB Vendors, Devices\n#" > ofile
	print "#\tGenerated by usb-header.awk, source data from the following URI:\n#\thttp://www.linux-usb.org/usb.ids\n#" > ofile
	print "#\tHeader created on " strftime( "%A, %d %b %Y %H:%M:%S %Z", systime() ) > ofile
	print "#endif" > ofile

	# and we start with vendors..
	print "\ntypedef struct _USB_VENTABLE\n{\n\tunsigned short\tVenId ;\n\tconst char *\tVenName ;\n}  USB_VENTABLE, *PUSB_VENTABLE ;\n" > ofile
	print "USB_VENTABLE\tUsbVenTable [] =\n{" > ofile
}

# matches vendor - starts with an id as first thing on the line
# because this occurs first in the header file, we output it without worry
/^[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] / { 

	if ( vendorcount++ > 0 ) { 
		formatting = ",\n"
	} else {
		formatting = ""
	}

	# store vendor ID for possible devices afterwards
	vendorid = $1
	vendor = substr($0, 7)
	gsub( /\"/, "&&", vendor )

	# Remove double interrogation points that may be interpreted as trigraphs
	gsub( /\?\?\?/, "xxx", vendor )
	gsub( /\?\?/, "xx", vendor )

	printf "%s", formatting "\t{ 0x" vendorid ", \"" vendor "\" }" > ofile
}

# matches device 
/^\t[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] / { 

	device = substr($0, 8)
	gsub( /\\/, "&&", device )
	gsub( /\"/, "&&", device )

	# Remove double interrogation points that may be interpreted as trigraphs
	gsub( /\?\?\?/, "xxx", device )
	gsub( /\?\?/, "xx", device )

	# store device ID for possible devices afterwards
	deviceid = $1
	devicecount++
	devices[devicecount, 1] = vendorid
	devices[devicecount, 2] = $1
	devices[devicecount, 3] = device 
}

# match device class - store data for later
/^C [[:xdigit:]][[:xdigit:]]  / {

	class = $2
	classname = substr($0, 7)
	gsub( /\"/, "\\\"", classname )

	inClassesDefinition = 1

	classcount++
	classes[classcount, 1] = class
	classes[classcount, 2] = "00"
	classes[classcount, 3] = "00"
	classes[classcount, 4] = classname
	classes[classcount, 5] = ""
	classes[classcount, 6] = ""
}

# match subclass, use device class data captured earlier, and output
inClassesDefinition && (/^\t[[:xdigit:]][[:xdigit:]]  /) {
	subclass = $1
	subclassname = substr($0, 6)
	gsub( /\"/, "\\\"", subclassname )

	classcount++
	classes[classcount, 1] = class
	classes[classcount, 2] = subclass
	classes[classcount, 3] = "00"
	classes[classcount, 4] = classname
	classes[classcount, 5] = subclassname
	classes[classcount, 6] = ""
}

# match protocol
inClassesDefinition && (/^\t\t[[:xdigit:]][[:xdigit:]]  /) {

	protocol = $1
	protocolname = substr($0, 7)
	gsub( /\"/, "\\\"", protocolname )

	classcount++
	classes[classcount, 1] = class
	classes[classcount, 2] = subclass
	classes[classcount, 3] = protocol
	classes[classcount, 4] = classname
	classes[classcount, 5] = subclassname
	classes[classcount, 6] = protocolname
}

#match comments
/^#/ {
	inClassesDefinition = false
}


# We've processed the file, now output.
END {

	print "\n};\n\n// Use this value for loop control during searching:\n#define\tUSB_VENTABLE_LEN\t(sizeof(UsbVenTable)/sizeof(USB_VENTABLE))\n" > ofile

	if ( devicecount > 0 ) {

		print "typedef struct _USB_DEVTABLE\n{\n\tunsigned short	VenId ;\n\tunsigned short	DevId ;\n\tconst char *\tChipDesc ;\n}  USB_DEVTABLE, *PUSB_DEVTABLE ;\n"  > ofile
		print "USB_DEVTABLE\tUsbDevTable [] =\n{" > ofile
		for (i = 1; i <= devicecount; i++) {

			if (i != 1) {
				formatting = ",\n"
			} else {
				formatting = ""
			}
			printf "%s", formatting "\t{ 0x" devices[i, 1] ", 0x" devices[i, 2] ", \"" devices[i, 3] "\" }" > ofile
		}
		print "\n} ;\n\n// Use this value for loop control during searching:\n#define	USB_DEVTABLE_LEN	(sizeof(UsbDevTable)/sizeof(USB_DEVTABLE))\n" > ofile

	}
	
	if ( classcount > 0 ) {
		print "typedef struct _USB_CLASSCODETABLE\n{\n\tunsigned char	BaseClass ;\n\tunsigned char	SubClass ;\n\tunsigned char	Protocol ;" > ofile
		print "\tconst char *\t\tBaseDesc ;\n\tconst char *\t\tSubDesc ;\n\tconst char *\t\tProtocolDesc ;\n}  USB_CLASSCODETABLE, *PUSB_CLASSCODETABLE ;\n" > ofile
		print "USB_CLASSCODETABLE UsbClassCodeTable [] =\n{" > ofile
		currentclass = classes[1, 1]
		for (i = 1; i <= classcount; i++) {

			if (i != 1) {
				formatting = ",\n"
			} else {
				formatting = ""
			}

			# pretty print separate classes
			if ( currentclass != classes[i, 1] ) {
				formatting = formatting "\n"
				currentclass = classes[i, 1]
			}

			# if the next item has the same details, we know we're to skip ourselves
			# this is because the programming interface name needs to be used, and we dont have it ourselves
			if ( ( classes[i, 1] != classes[i+1, 1] ) || ( classes[i, 2] != classes[i+1, 2] ) || ( classes[i, 3] != classes[i+1, 3] ) ) {
				printf formatting "\t{ 0x" classes[i, 1] ", 0x" classes[i, 2] ", 0x" classes[i, 3] ", \"" classes[i, 4] "\", \"" classes[i, 5]  "\", \"" classes[i, 6] "\" }" > ofile
			}
		}
		print "\n} ;\n\n// Use this value for loop control during searching:\n#define	USB_CLASSCODETABLE_LEN	(sizeof(UsbClassCodeTable)/sizeof(USB_CLASSCODETABLE))\n" > ofile

	}


	close(ofile)
}


